package top.jle.locker.integration;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.noear.solon.Solon;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.util.RunnableEx;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import top.jle.locker.config.LockerConfig;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class LockerImpl implements Locker{
    //加锁等待时间
    public static final Integer LOCK_WAIT_TIME = 5;

    private static final String DEFAULT_LOCK_PREFIX = "lock";

    @Inject
    private RedissonClient redissonClient;
    @Inject
    private LockerConfig lockerConfig;



    @Override
    public void lock(String lockKey, long timeout, RunnableEx runnableEx) {
        RLock lock = this.getLock(null,lockKey);
        this.lock(lock,timeout,runnableEx);
    }

    private void printLog(String format, Object... args) {
        if(lockerConfig.isLog()){
            log.info(format,args);
        }
    }
    @Override
    public boolean tryLock(String prefix, String lockKey, long timeout) {
        RLock lock = this.getLock(prefix,lockKey);
        try {
            return lock.tryLock(LOCK_WAIT_TIME,timeout, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }


    private void lock(RLock lock,long timeout, RunnableEx runnableEx){
        boolean lockSuccess = false;
        try{
            lockSuccess = lock.tryLock(LOCK_WAIT_TIME,timeout, TimeUnit.SECONDS);
            if(lockSuccess){
                printLog("加锁成功，开始执行任务 lockKey={}",lock.getName());
                runnableEx.run();
            }
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            this.releaseLock(lock,lockSuccess);
        }
    }
    @Override
    public void lock(String lockKey, RunnableEx runnableEx) {
        RLock lock = this.getLock(null,lockKey);
        this.lock(lock,-1,runnableEx);
    }
    @Override
    public void lock(String prefix, String lockKey, long timeout, RunnableEx runnableEx) {
        RLock lock = this.getLock(prefix,lockKey);
        this.lock(lock,timeout,runnableEx);
    }
    @Override
    public void lock(String prefix, String lockKey, RunnableEx runnableEx) {
        RLock lock = this.getLock(prefix,lockKey);
        this.lock(lock,-1,runnableEx);
    }
    @Override
    public RLock getLock(String prefix, String lockKey) {
        if(StringUtils.isBlank(lockKey)) {
            throw new IllegalArgumentException("lockKey不能为空");
        }
        //处理加锁前缀
        prefix = Optional.ofNullable(prefix)
                .filter(StringUtils::isNotBlank)
                .orElseGet(()-> Optional.ofNullable(Solon.cfg().appName()).filter(StringUtils::isNotBlank).orElse(DEFAULT_LOCK_PREFIX));
        //拼接完整的锁名
        String key = prefix.endsWith(":")?(prefix + lockKey):(prefix+":"+lockKey);
        RLock lock = redissonClient.getLock(key);
        printLog("获取锁对象，lockKey={}", lock.getName());
        return lock;
    }
    private void releaseLock(RLock lock,boolean lockSuccess) {
        if(lock!=null && lockSuccess){
            //释放锁 unlock做了兜底不会越权释放锁 所以lock.isLocked()和lock.isHeldByCurrentThread()不需要判断
            //若执行lock.isLocked()时redis不可用则会导致使用看门狗的情况无法释放锁
            lock.unlock();
            printLog("释放锁成功，lockKey={}",lock.getName());
        }
    }
}
